package day30.zqStream;

import java.io.*;

/**
 * 测试Java IO流的类
 * 演示InputStreamReader、OutputStreamWriter等字符流的使用
 * 
 * Java IO流是Java中处理输入输出的核心机制，它将数据的读写操作抽象为流的形式。
 * IO流可以分为以下几类：
 * 1. 按数据流向分类：
 *    - 输入流（InputStream/Reader）：从数据源读取数据到程序
 *    - 输出流（OutputStream/Writer）：将程序中的数据写入到目标位置
 * 2. 按处理数据单位分类：
 *    - 字节流：以字节为单位处理数据，适用于处理二进制文件（如图片、音频等）
 *      主要基类：InputStream和OutputStream
 *    - 字符流：以字符为单位处理数据，适用于处理文本文件
 *      主要基类：Reader和Writer
 * 3. 按功能分类：
 *    - 节点流：直接与数据源或目标连接的流
 *    - 处理流：对其他流进行包装，提供更多功能的流
 */
public class testIOStreams {
    /**
     * 程序入口点方法
     * main方法是Java程序的执行起点，JVM会首先调用这个方法来启动程序
     * 
     * @param args 命令行参数数组，用于接收从命令行传入的参数
     *             在这个程序中未使用该参数
     */
    public static void main(String[] args) {
        // 打印程序开始标识，通知用户测试已经开始
        System.out.println("=== Java IO流测试 ===\n");
        
        // 1. 字节流与字符流转换测试
        // 调用testStreamReaderWriter方法测试InputStreamReader和OutputStreamWriter的使用
        // 这两个类是字节流到字符流的桥梁，可以指定字符编码进行读写操作
        testStreamReaderWriter();
        
        // 2. 缓冲流测试
        // 调用testBufferedStreams方法测试BufferedReader和BufferedWriter的使用
        // 缓冲流通过在内存中创建缓冲区来提高IO操作的效率
        testBufferedStreams();
        
        // 3. 打印流测试
        // 调用testPrintStream方法测试PrintStream的使用
        // PrintStream提供了格式化输出的功能，是System.out对象的类型
        testPrintStream();
        
        // 4. 数据流测试
        // 调用testDataStream方法测试DataInputStream和DataOutputStream的使用
        // 数据流允许按数据类型读写，保证数据的类型和值都正确无误
        testDataStream();
        
        // 打印程序结束标识，通知用户测试已经完成
        System.out.println("\n=== 测试完成 ===");
    }
    
    /**
     * 测试InputStreamReader和OutputStreamWriter的使用
     * 
     * InputStreamReader和OutputStreamWriter是字节流到字符流的桥梁：
     * - InputStreamReader：将字节输入流转换为字符输入流，可以指定字符编码
     * - OutputStreamWriter：将字符输出流转换为字节输出流，可以指定字符编码
     * 
     * 这两个类解决了不同字符编码环境下文本读写的问题，是处理文本文件的重要工具
     */
    private static void testStreamReaderWriter() {
        // 打印当前测试项目标识，让用户知道正在进行哪一部分的测试
        System.out.println("1. InputStreamReader和OutputStreamWriter测试:");
        
        // 使用try-catch语句处理可能发生的IO异常
        // IO操作可能会因为文件不存在、权限不足、磁盘已满等原因而失败
        try {
            // 创建文件输出流并包装为OutputStreamWriter
            // FileOutputStream：文件字节输出流，用于将数据写入文件
            // 参数"io_test.txt"指定了要写入的文件名
            FileOutputStream fos = new FileOutputStream("io_test.txt");
            
            // OutputStreamWriter：字节流通向字符流的桥梁
            // 第一个参数是字节输出流，第二个参数"UTF-8"指定了字符编码
            // UTF-8是一种变长的字符编码，能表示Unicode标准中的任何字符
            OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
            
            // 使用OutputStreamWriter写入文本内容
            // write方法将指定的字符串写入到流中，但数据可能还在缓冲区中
            osw.write("这是一行中文测试文本\n");
            // 写入英文文本，演示不同语言的处理
            osw.write("This is a line of English test text\n");
            // 写入包含数字的文本，演示数字字符的处理
            osw.write("数字测试: 12345\n");
            
            // 关闭流，释放相关资源
            // close方法会先刷新缓冲区（将缓冲区中的数据写入文件），然后关闭流
            // 关闭流后不能再进行读写操作
            osw.close();
            
            // 创建文件输入流并包装为InputStreamReader
            // FileInputStream：文件字节输入流，用于从文件读取数据
            FileInputStream fis = new FileInputStream("io_test.txt");
            
            // InputStreamReader：字节流通向字符流的桥梁
            // 第一个参数是字节输入流，第二个参数"UTF-8"指定了字符编码
            // 必须使用与写入时相同的编码，否则会出现乱码
            InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
            
            // 读取文本并打印到控制台
            System.out.println("  从文件读取的内容:");
            
            // 逐个字符读取文件内容
            // read()方法每次读取一个字符，返回字符的int值，如果到达流的末尾则返回-1
            int ch;
            // 循环读取字符直到文件末尾
            while ((ch = isr.read()) != -1) {
                // 将int值转换为char类型并打印
                // 在前面添加缩进空格以保持输出格式整齐
                System.out.print("    " + (char) ch);
            }
            
            // 关闭输入流，释放相关资源
            isr.close();
            // 打印换行符，为下一部分输出做准备
            System.out.println();
            
        } catch (IOException e) {
            // 捕获并处理IO异常
            // 当IO操作出现问题时会执行这里的代码
            System.err.println("  IO流测试过程中发生错误: " + e.getMessage());
            // 可以选择重新抛出异常或者进行其他错误处理
        }
    }
    
    /**
     * 测试BufferedReader和BufferedWriter的使用
     * 
     * 缓冲流通过在内存中创建缓冲区来提高IO操作的效率：
     * - BufferedReader：为字符输入流添加缓冲功能，提供按行读取等便利方法
     * - BufferedWriter：为字符输出流添加缓冲功能，提供按行写入等便利方法
     * 
     * 缓冲流的优点：
     * 1. 减少实际的物理读写次数，提高IO性能
     * 2. 提供更便利的读写方法，如readLine()和newLine()
     * 3. 自动管理缓冲区，简化编程
     */
    private static void testBufferedStreams() {
        // 打印当前测试项目标识
        System.out.println("\n2. 缓冲流测试:");
        
        // 使用try-catch语句处理可能发生的IO异常
        try {
            // 创建BufferedWriter
            // FileWriter：文件字符输出流，可以直接写入字符到文件
            FileWriter fw = new FileWriter("buffer_test.txt");
            
            // BufferedWriter：为字符输出流添加缓冲功能
            // 缓冲区默认大小为8192个字符，可以显著提高写入效率
            BufferedWriter bw = new BufferedWriter(fw);
            
            // 使用BufferedWriter写入多行文本
            // write方法将指定的字符串写入缓冲区
            bw.write("第一行文本");
            // newLine方法写入一个行分隔符，根据系统不同可能是\n或\r\n
            bw.newLine();
            // 继续写入第二行文本
            bw.write("第二行文本");
            // 再次写入行分隔符
            bw.newLine();
            // 写入第三行文本
            bw.write("第三行文本");
            
            // 刷新并关闭流
            // flush方法将缓冲区中的数据强制写入文件
            bw.flush();
            // close方法会先调用flush方法，然后关闭流
            bw.close();
            
            // 创建BufferedReader
            // FileReader：文件字符输入流，可以直接从文件读取字符
            FileReader fr = new FileReader("buffer_test.txt");
            
            // BufferedReader：为字符输入流添加缓冲功能
            // 提供readLine方法可以按行读取文本，非常方便
            BufferedReader br = new BufferedReader(fr);
            
            // 读取文本行并打印
            System.out.println("  使用BufferedReader读取的行:");
            
            // 按行读取文件内容
            // readLine方法读取一行文本，不包括行终止符，如果到达流末尾则返回null
            String line;
            while ((line = br.readLine()) != null) {
                // 打印读取到的每一行，在前面添加缩进空格保持格式整齐
                System.out.println("    " + line);
            }
            
            // 关闭BufferedReader，同时也会关闭内部的FileReader
            br.close();
            
        } catch (IOException e) {
            // 捕获并处理IO异常
            System.err.println("  缓冲流测试过程中发生错误: " + e.getMessage());
        }
    }
    
    /**
     * 测试PrintStream的使用
     * 
     * PrintStream是字节输出流的一个子类，提供了格式化输出的功能：
     * - 可以方便地打印各种数据类型
     * - 提供print和println方法，自动进行类型转换
     * - 提供printf方法，支持格式化输出
     * - 不会抛出IOException，而是设置内部错误标志
     * 
     * System.out和System.err都是PrintStream的实例
     */
    private static void testPrintStream() {
        // 打印当前测试项目标识
        System.out.println("\n3. PrintStream测试:");
        
        // 使用try-catch语句处理可能发生的IO异常
        try {
            // 创建PrintStream
            // FileOutputStream：文件字节输出流，用于将数据写入文件
            FileOutputStream fos = new FileOutputStream("print_test.txt");
            
            // PrintStream：提供格式化打印功能的输出流
            // 可以方便地输出各种数据类型，是System.out的类型
            PrintStream ps = new PrintStream(fos);
            
            // 使用PrintStream输出各种类型数据
            // println方法会自动在输出后添加行分隔符
            ps.println("PrintStream测试");
            // 输出整数类型数据
            ps.println(12345);
            // 输出浮点数类型数据
            ps.println(3.14159);
            // 输出布尔类型数据
            ps.println(true);
            // 使用printf方法进行格式化输出
            // %s表示字符串，%d表示整数，%.2f表示保留两位小数的浮点数
            ps.printf("格式化输出: %s - %d - %.2f\n", "测试", 100, 25.8);
            
            // 关闭PrintStream
            ps.close();
            
            // 读取并显示PrintStream输出的内容
            // 使用BufferedReader按行读取文件内容
            BufferedReader br = new BufferedReader(new FileReader("print_test.txt"));
            System.out.println("  PrintStream输出的内容:");
            
            // 按行读取并打印内容
            String line;
            while ((line = br.readLine()) != null) {
                // 在前面添加缩进空格保持格式整齐
                System.out.println("    " + line);
            }
            
            // 关闭BufferedReader
            br.close();
            
        } catch (IOException e) {
            // 捕获并处理IO异常
            System.err.println("  PrintStream测试过程中发生错误: " + e.getMessage());
        }
    }
    
    /**
     * 测试DataInputStream和DataOutputStream的使用
     * 
     * 数据流允许按数据类型读写，保证数据的类型和值都正确无误：
     * - DataOutputStream：允许以机器无关的方式将Java基本数据类型写入输出流
     * - DataInputStream：允许以机器无关的方式从输入流中读取Java基本数据类型
     * 
     * 数据流的特点：
     * 1. 保证数据的类型正确性
     * 2. 保证数据的值正确性
     * 3. 读写顺序必须一致，先写入的先读取
     * 4. 适用于需要精确保存和恢复数据的场景
     */
    private static void testDataStream() {
        // 打印当前测试项目标识
        System.out.println("\n4. DataInputStream和DataOutputStream测试:");
        
        // 使用try-catch语句处理可能发生的IO异常
        try {
            // 创建DataOutputStream
            // FileOutputStream：文件字节输出流，用于将数据写入文件
            FileOutputStream fos = new FileOutputStream("data_test.txt");
            
            // DataOutputStream：允许将Java基本数据类型写入输出流
            // 以平台无关的方式写入数据，保证在不同平台上读取时的一致性
            DataOutputStream dos = new DataOutputStream(fos);
            
            // 写入各种类型数据
            // writeInt方法将一个int值以4个字节的形式写入
            dos.writeInt(12345);
            // writeDouble方法将一个double值以8个字节的形式写入
            dos.writeDouble(3.14159);
            // writeBoolean方法将一个boolean值以1个字节的形式写入
            dos.writeBoolean(true);
            // writeUTF方法将一个字符串以UTF-8格式写入
            dos.writeUTF("UTF字符串测试");
            
            // 关闭DataOutputStream
            dos.close();
            
            // 创建DataInputStream
            // FileInputStream：文件字节输入流，用于从文件读取数据
            FileInputStream fis = new FileInputStream("data_test.txt");
            
            // DataInputStream：允许从输入流中读取Java基本数据类型
            // 以平台无关的方式读取数据，与DataOutputStream配合使用
            DataInputStream dis = new DataInputStream(fis);
            
            // 按顺序读取各种类型数据
            // 读取顺序必须与写入顺序完全一致，否则会读取到错误的数据
            // readInt方法读取一个int值
            int intValue = dis.readInt();
            // readDouble方法读取一个double值
            double doubleValue = dis.readDouble();
            // readBoolean方法读取一个boolean值
            boolean boolValue = dis.readBoolean();
            // readUTF方法读取一个UTF-8格式的字符串
            String utfString = dis.readUTF();
            
            // 关闭DataInputStream
            dis.close();
            
            // 显示读取的数据
            System.out.println("  从DataOutputStream写入并由DataInputStream读取的数据:");
            // 打印读取到的int值
            System.out.println("    int值: " + intValue);
            // 打印读取到的double值
            System.out.println("    double值: " + doubleValue);
            // 打印读取到的boolean值
            System.out.println("    boolean值: " + boolValue);
            // 打印读取到的UTF字符串
            System.out.println("    UTF字符串: " + utfString);
            
        } catch (IOException e) {
            // 捕获并处理IO异常
            System.err.println("  数据流测试过程中发生错误: " + e.getMessage());
        }
    }
}